import logging
import os
from pathlib import Path

import yaml

BASE_DIR = Path(__file__).resolve().parent.parent


class Loader(yaml.SafeLoader):
    def __init__(self, stream):
        self._root = os.path.split(stream.name)[0]
        super().__init__(stream)

    def include(self, node):
        filename = os.path.join(self._root, self.construct_scalar(node))
        with open(filename, encoding="utf8") as fr:
            return yaml.load(fr, Loader)


Loader.add_constructor("!include", Loader.include)


class LoadConfigHandler:

    env = os.getenv("FLASK_CONFIG", "development")

    def __init__(self):
        self.load_info = {}

    def load(self, path=None):
        """
        Automatically load the corresponding configuration file contents of the FLASK_CONFIG environment variable.  # noqa
        """
        path = path or BASE_DIR / f"configs/{self.env}.configs.yaml"
        try:
            with open(path, encoding="utf8") as f:
                config = yaml.load(f, Loader=Loader)
                os.environ["FLASK_DEBUG"] = f'{config.get("DEBUG", "1")}'
        except FileNotFoundError as e:
            logging.error(e)
            logging.error("Please add a configs.yaml file")
            exit(0)

        return config

    def __call__(self, path=None):
        if self.load_info.get(path, None) is None:
            self.load_info[path] = self.load(path=path)
        return self.load_info[path]


load = LoadConfigHandler()


# 20220815暂时废弃，完成某赛题flag回答得到其他几部分分值可参考该处
# def load_image_preset_config(path_dir: str, redis_conn: Redis):
#     """项目启动时预加载镜像flag供靶场使用"""
#
#     from utils.crypto import Crypt
#
#     default_score = 10
#     key = "LOAD_IMAGE_PRESET_FLAG_LOCK_KET::RED_BLUE"
#
#     # 目的在于防止项目启动时多进程或多次初始化app触发多次，只允许第一次执行而不是with
#     lock = redis_conn.lock(key, timeout=60)
#     if lock.locked():
#         return
#     else:
#         lock.acquire()
#
#     preset_dir = Path(path_dir)
#     if not preset_dir.exists():
#         lock.release()
#         return None
#
#     try:
#         config = load(str(preset_dir.absolute()))
#         images_list = list(config.keys())
#
#         for image_id, image_flags in config.items():
#             image_preset_flag_key = f"IMAGE_PRESET_FLAG::{image_id}"
#
#             flag_list = []
#             for item in image_flags["list"]:
#                 # 针对flag为空的漏洞
#                 if item["flag"] == "":
#                     flag_list.append(
#                         {
#                             "flag": "",
#                             "image_id": image_id,
#                             "flag_uuid": uuid.uuid1().hex,
#                             "describe": item.get("describe", ""),
#                             "score": item.get("score", default_score),
#                             "is_master": item.get("is_master", False),
#                             # FIXME mysql varchar有效支持 1 << 269, 镜像超过269时需优化
#                             #  如果出现某PLC攻停获得整个场景全部flag得分的情况，使用单独字段标识
#                             #  或 使用image_mark and flag_mark分层进行灵活标记可支持269 * 269种情况
#                             "mark": f"{1 << images_list.index(image_id)}",
#                         }
#                     )
#                     continue
#                 # 该循环通常循环1-2次
#                 for val in item["flag"]:
#                     flag_list.append(
#                         {
#                             "flag": val,
#                             "image_id": image_id,
#                             "flag_uuid": uuid.uuid1().hex,
#                             "describe": item.get("describe", ""),
#                             "score": item.get("score", default_score),
#                             "is_master": item.get("is_master", False),
#                             "mark": f"{1 << images_list.index(image_id)}",
#                         }
#                     )
#
#             redis_conn.set(
#                 image_preset_flag_key,
#                 Crypt.encrypt(
#                     json.dumps({"name": image_flags["name"], "flag_list": flag_list})
#                 ),
#             )
#     except Exception as e:
#         logging.exception("加载预设flag失败", exc_info=e)
#         lock.release()
